package com.ggshily.swf.abc;

import java.io.DataInput;
import java.io.IOException;

public class ABCTraitInfo
{
	public static final int TYPE_Slot = 0;
	public static final int TYPE_Method = 1;
	public static final int TYPE_Getter = 2;
	public static final int TYPE_Setter = 3;
	public static final int TYPE_Class = 4;
	public static final int TYPE_Function = 5;
	public static final int TYPE_Const = 6;
	
	public static final int ATTR_Final = 0x1;
	public static final int ATTR_Override = 0x2;
	public static final int ATTR_Metadata = 0x4;
	
	/**
	 * U30, an index into the <code>multiname</code> array of the constant pool; it provide a name for the trait.<br>
	 * The value can not be zero, and the <code> multiname</code> entry specified must be a QName.
	 */
	public int name;
	
	/**
	 * U8, contains two four-bit fields. The lower four bits determine the kind of this trait.<br>
	 * The upper four bits comprise a bit vector providing attributes of the trait.<br>
	 */
	public short kind;
	
	public byte[] data;
	
	/**
	 * U30, this field are present only if <code>ATTR_Metadata</code> is present in the upper four bits of the <code>kind</code> field.<br>
	 * This field is the number of entries in the <code>metadata</code> array.
	 */
	public int metadataCount;
	
	/**
	 * U30 metadata[metataCount], this field are present only if <code>ATTR_Metadata</code> is present in the upper four bits of the <code>kind</code> field.<br>
	 * This array contains indices into the <code>metadata</code> array of the <code>abcFile</code>
	 */
	public int[] meatadatas;
	
	/**
	 * A <code>kind</code> value of <code>Trait_Slot</code>(0) or <code>Trait_Const</code>(6) requires this field.
	 */
	public TraitSlot traitSlot;
	
	/**
	 * A <code>kind</code> value of <code>Trait_Class</code>(0x04) requires this field.
	 */
	public TraitClass traitClass;

	/**
	 * A <code>kind</code> value of <code>Trait_Function</code>(0x05) requires this field.
	 */
	public TraitFunction traitFunction;
	
	/**
	 * A <code>kind</code> value of <code>Trait_Method</code>(0x01), <code>Trait_Getter</code>(0x02) or <code>Trait_Setter</code>(0x03) requires this field.
	 */
	public TraitMethod traitMethod;
	
//	private ABCFile file;
//	private String desc;
	
	public ABCTraitInfo(DataInput di) throws IOException
	{
		parseInput(di);
	}

	private void parseInput(DataInput di) throws IOException
	{
		name = ABCUtil.readU30(di);
		kind = ABCUtil.readU8(di);
		
		int type = kind & 0xF;
		switch(type)
		{
		case TYPE_Slot:
		case TYPE_Const:
			traitSlot = new TraitSlot(di);
			break;
		case TYPE_Class:
			traitClass = new TraitClass(di);
			break;
		case TYPE_Function:
			traitFunction = new TraitFunction(di);
			break;
		case TYPE_Method:
		case TYPE_Getter:
		case TYPE_Setter:
			traitMethod = new TraitMethod(di);
			break;
		}
		
		if (((kind >> 4) & ATTR_Metadata) > 0)
		{
			metadataCount = ABCUtil.readU30(di);
			meatadatas = new int[metadataCount];
			for (int i = 0; i < metadataCount; i++)
			{
				meatadatas[i] = ABCUtil.readU30(di);
			}
		}
	}

	public ABCTraitInfo(DataInput di, ABCFile file) throws IOException
	{
//		this.file = file;

		parseInput(di);
	}
}

class TraitSlot
{
	/**
	 * U30, This field is an integer from 0 to N and is used to identify a position in which this trait resides.
	 * A value of 0 requests the AVM2 to assign a position.
	 */
	public int slotId;
	
	/**
	 * U30, This field is used to identify the type of the trait. It is an index into the <code>multiname</code> array of the
	 * <code>constant_pool</code>. A value of zero indicates that the type is the any type(*).
	 */
	public int typeName;
	
	/**
	 * U30, This field is an index that is used in conjunction with the <code>vkind</code> field in order to define a value for
	 * the trait. If it is 0, <code>vkind</code> is empty; otherwise it references one of the tables in the constant pool,
	 * depending on the value of <code>vkind</code>
	 */
	public int vindex;
	
	/**
	 * U8, This field exists only when <code>vindex</code> is non-zero. It is used to deremine how <code>vindex</code> will
	 * be interpreted. See the "Constant Kind" table above for details.
	 */
	public short vkind;
	
	public TraitSlot(DataInput di) throws IOException
	{
		slotId = ABCUtil.readU30(di);
		typeName = ABCUtil.readU30(di);
		vindex = ABCUtil.readU30(di);
		if(vindex > 0)
		{
			vkind = ABCUtil.readU8(di);
		}
	}
}

class TraitClass
{
	/**
	 * U30, This field is an integer from 0 to N and is used to identify a position in which this trait resides.
	 * A value of 0 requests the AVM2 to assign a position.
	 */
	public int slotId;
	
	/**
	 * U30, This field is an index that points into the <code>class</code> array of the <code>abcFile</code> entry.
	 */
	public int classi;
	
	public TraitClass(DataInput di) throws IOException
	{
		slotId = ABCUtil.readU30(di);
		classi = ABCUtil.readU30(di);
	}
}

class TraitFunction
{
	/**
	 * U30, This field is an integer from 0 to N and is used to identify a position in which this trait resides.
	 * A value of 0 requests the AVM2 to assign a position.
	 */
	public int slotId;
	
	/**
	 * U30, This field is an index that points into the <code>method</code> array of the <code>abcFile</code> entry.
	 */
	public int function;
	
	public TraitFunction(DataInput di) throws IOException
	{
		slotId = ABCUtil.readU30(di);
		function = ABCUtil.readU30(di);
	}
}

class TraitMethod
{
	/**
	 * U30, This field is a compiler assigned integer that is used by AVM2 to optimize the resolution of virtual function calls.
	 * An overridden method must have the same <code>disp_id</code> as that of the method in the base class.
	 * A value of zero disables this optimization.
	 */
	public int dispId;
	
	/**
	 * U30, This field is an index that points into the <code>method</code> array of the <code>abcFile</code> entry.
	 */
	public int method;
	
	public TraitMethod(DataInput di) throws IOException
	{
		dispId = ABCUtil.readU30(di);
		method = ABCUtil.readU30(di);
	}
}
